package cn.xo68.boot.auth.server.web;

import cn.xo68.boot.auth.core.cookie.MapCookieTemplate;
import cn.xo68.boot.auth.core.domain.Oauth2Principal;
import cn.xo68.boot.auth.core.properties.OAuthResourceProperties;
import cn.xo68.boot.auth.server.domain.LoginFailLimit;
import cn.xo68.boot.auth.server.domain.OauthToken;
import cn.xo68.boot.auth.server.domain.OauthUser;
import cn.xo68.boot.auth.server.properties.AuthServerProperties;
import cn.xo68.boot.auth.server.service.LoginFailLimitService;
import cn.xo68.boot.auth.server.service.OauthTokenService;
import cn.xo68.boot.auth.server.service.OauthUserService;
import cn.xo68.boot.web.web.WebContext;
import cn.xo68.core.date.DateTime;
import cn.xo68.core.entity.JsonResponseEntityBuilder;
import cn.xo68.core.util.StringTools;
import org.apache.oltu.oauth2.as.issuer.MD5Generator;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuer;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import javax.servlet.DispatcherType;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;

@Controller
public class LoginController {

    private static  final Logger logger= LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private MapCookieTemplate browerDataCookieTemplate;

    @Autowired
    private AuthServerProperties authServerProperties;

    @Autowired
    private OauthUserService oauthUserService;
    @Autowired
    private OAuthResourceProperties oAuthResourceProperties;


    @Autowired
    private OauthTokenService oauthTokenService;

    @Autowired
    private SimpleCookie accessTokenCookie;

    @Autowired
    private WebContext webContext;

    @Autowired
    private LoginFailLimitService loginFailLimitService;

    @GetMapping("/login")
    public String login(HttpServletRequest request, HttpServletResponse response,Model model,Subject subject, Oauth2Principal oauth2Principal){
        //Subject subject = SecurityUtils.getSubject();

        String redirectUrl=request.getParameter(OAuth.OAUTH_REDIRECT_URI);
        if(StringTools.isNotEmpty(redirectUrl)){
            model.addAttribute(OAuth.OAUTH_REDIRECT_URI, redirectUrl);
            HashMap<String,String> dataMap=new HashMap<>();
            dataMap.put(OAuth.OAUTH_REDIRECT_URI, redirectUrl);
            browerDataCookieTemplate.setValue(dataMap);
            browerDataCookieTemplate.save(request,response);
        }
        return "login";
    }

    @PostMapping("/login")
    public ResponseEntity loginSubmit(HttpServletRequest request, HttpServletResponse response, Model model, Oauth2Principal oauth2Principal){
        Subject subject = SecurityUtils.getSubject();
        String userName=request.getParameter("username");
        String password=request.getParameter("password");

        MediaType mediaType=MediaType.APPLICATION_JSON_UTF8;
        HttpHeaders headers = webContext.buildHeaders(false,null);

        LoginFailLimit loginFailLimit=loginFailLimitService.get(userName);
        if(loginFailLimit.getFailTimes()>= authServerProperties.getLoginFailMaxTimes()){
            return ResponseEntity
                    .status(HttpStatus.OK)
                    .headers(headers)
                    .contentType(mediaType)
                    .body(JsonResponseEntityBuilder.fail().setError("登录错误次数过多").build("登录失败"));
        }


        OauthUser oauthUser = oauthUserService.getByAccount(userName);

        if(oauthUser !=null && oauthUser.getUserPass().equals(password)){
            OAuthIssuer oAuthIssuer=new OAuthIssuerImpl(new MD5Generator());
            try {
                if(loginFailLimit.getFailTimes()>0){
                    loginFailLimitService.delete(userName);
                }

                String accessToken=oAuthIssuer.accessToken();

                //持久化令牌信息及相关信息
                OauthToken oauthToken=new OauthToken();
                oauthToken.setToken(accessToken);
                oauthToken.setUserId(oauthUser.getUserId());
                oauthToken.setAccount(oauthUser.getAccount());
                oauthToken.setUserName(oauthUser.getUserName());
                oauthToken.setCreateTime(DateTime.Now().getDate());
                oauthToken.setExpireTime(DateTime.Now().addSecond(authServerProperties.getTokenExpireSeconds()).getDate());
                oauthTokenService.insert(oauthToken);

                accessTokenCookie.setValue(accessToken);
                accessTokenCookie.saveTo(request,response);

                logger.debug("access_token: {}", accessToken);

                // 如果是认证端点重定向的进行特殊处理
                if (request.getDispatcherType() == DispatcherType.FORWARD) {
                    String requestUri=webContext.getRequestFullUri(request, true);
                    try {
                        headers.setLocation(new URI(requestUri));
                    } catch (URISyntaxException e) {
                        logger.error("请求地址转换异常", e);
                    }

                    return ResponseEntity
                            .status(HttpStatus.FOUND)
                            .headers(headers)
                            .contentType(mediaType)
                            .build();
                }



                String redirectUrl=request.getParameter(OAuth.OAUTH_REDIRECT_URI);
                if(StringTools.isNotEmpty(redirectUrl)){
                    return ResponseEntity
                            .status(HttpStatus.OK)
                            .headers(headers)
                            .contentType(mediaType)
                            .body(JsonResponseEntityBuilder.ok().build(redirectUrl));
                }

                browerDataCookieTemplate.load(request);
                HashMap<String, String> dataMap = browerDataCookieTemplate.getValue();
                if(dataMap!=null){
                    redirectUrl=dataMap.get(OAuth.OAUTH_REDIRECT_URI);
                    if(StringTools.isNotEmpty(redirectUrl)){
                        return ResponseEntity
                                .status(HttpStatus.OK)
                                .headers(headers)
                                .contentType(mediaType)
                                .body(JsonResponseEntityBuilder.ok().build(redirectUrl));
                    }
                }

                return ResponseEntity
                        .status(HttpStatus.OK)
                        .headers(headers)
                        .contentType(mediaType)
                        .body(JsonResponseEntityBuilder.ok().build(authServerProperties.getSuccessUrl()));
            } catch (OAuthSystemException e) {
                logger.error("登录失败", e);
                model.addAttribute("error","登录失败");
                model.addAttribute("msg","生成令牌异常");
                return ResponseEntity
                        .status(HttpStatus.OK)
                        .headers(headers)
                        .contentType(mediaType)
                        .body(JsonResponseEntityBuilder.fail().setError("生成令牌异常").build("登录失败"));
            }
        }else {
//            model.addAttribute("error","登录失败");
//            model.addAttribute("msg","用户名、密码错误");
            loginFailLimit.setLastDate(DateTime.Now().getDate());
            loginFailLimit.setFailTimes(loginFailLimit.getFailTimes()+1);
            loginFailLimitService.put(loginFailLimit);
            return ResponseEntity
                    .status(HttpStatus.OK)
                    .headers(headers)
                    .contentType(mediaType)
                    .body(JsonResponseEntityBuilder.fail().setError("用户名、密码错误").build("登录失败"));
        }

        //return "login";
    }


}
